package main

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/event"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"net/http"
	"time"
)

func main() {
	router := gin.Default()

	// 定义路由
	{
		router.POST("/users", userRegister)
		router.GET("/users/:id", queryUser)
		router.DELETE("/users/:id", deleteUser)
		router.GET("/assets/:id", queryAsset)
		router.POST("/assets/enroll", assetsEnroll)
		router.POST("/assets/exchange", assetsExchange)
		router.GET("/assets/exchange/history", assetsExchangeHistory)
	}

	router.Run(":4000")
}

type UserRegisterRequest struct {
	Id   string `form:"id" binding:"required"`
	Name string `form:"name" binding:"required"`
}

// 用户开户
func userRegister(ctx *gin.Context) {
	req := new(UserRegisterRequest)
	if err := ctx.ShouldBind(req); err != nil {
		ctx.AbortWithError(400, err)
		return
	}

	// 区块链交互
	resp, err := channelExecute("userRegister", [][]byte{
		[]byte(req.Name),
		[]byte(req.Id),
	})
	if err != nil {
		ctx.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	ctx.JSON(http.StatusOK, resp)
}

// 查询用户
func queryUser(ctx *gin.Context) {
	userId := ctx.Param("id")

	resp, err := channelQuery("queryUser", [][]byte{
		[]byte(userId),
	})

	if err != nil {
		ctx.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	ctx.JSON(http.StatusOK, resp)
}

// 用户销户
func deleteUser(ctx *gin.Context) {
	userId := ctx.Param("id")

	// 区块链交互
	resp, err := channelExecute("userRegister", [][]byte{
		[]byte(userId),
	})
	if err != nil {
		ctx.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	ctx.JSON(http.StatusOK, resp)
}

// 资产查询
func queryAsset(ctx *gin.Context) {
	userId := ctx.Param("id")

	resp, err := channelQuery("queryUser", [][]byte{
		[]byte(userId),
	})

	if err != nil {
		ctx.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	ctx.JSON(http.StatusOK, resp)
}

type AssetsEnrollRequest struct {
	AssetId   string `form:"assetsid" binding:"required"`
	AssetName string `form:"assetname" binding:"required"`
	Metadata  string `form:"metadata" binding:"required"`
	OwnerId   string `form:"ownerid" binding:"required"`
}

// 资产登记
func assetsEnroll(ctx *gin.Context) {
	userId := ctx.Param("id")
}

type AssetsExchangeRequest struct {
	AssetId        string `form:"assetsid" binding:"required"`
	OriginOwnerId  string `form:"originownerid" binding:"required"`
	CurrentOwnerId string `form:"currentownerid" binding:"required"`
}

// 资产转让
func assetsExchange(ctx *gin.Context) {
	userId := ctx.Param("id")
}

// 资产历史变更记录
func assetsExchangeHistory(ctx *gin.Context) {
	userId := ctx.Param("id")
}

var (
	sdk           *fabsdk.FabricSDK
	channelName   = "assetschannel"
	chaincodeName = "assets"
	org           = "SyOrg1"
	user          = "Admin"
	configPath    = "/Users/lcl/work/go/src/food_traceability/test/fabric/application/config.yaml"
	//configPath = "./config.yaml"
)

func init() {
	var err error
	fabsdk.New(config.FromFile(configPath))
	if err != nil {
		panic(err)
	}
}

// 区块链管理
func manageBlockchain() {
	// 表明身份
	ctx := sdk.Context(fabsdk.WithOrg(org), fabsdk.WithUser(user))

	cli, err := resmgmt.New(ctx)
	if err != nil {
		panic(err)
	}

	// 具体操作
	cli.SaveChannel(resmgmt.SaveChannelRequest{}, resmgmt.WithOrdererEndpoint("orerer.qklszzngsy.com"),
		resmgmt.WithTargetEndpoints())

}

// 区块链数据查询 账本的查询
func queryBlockchain() {
	// 表明身份
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))
	// 该cli主要用来查询信息
	cli, err := ledger.New(ctx)
	if err != nil {
		panic(err)
	}

	resp, err := cli.QueryInfo(ledger.WithTargetEndpoints("peer0.org1.qklszzngsy.com"))
	if err != nil {
		panic(err)
	}

	fmt.Println(resp)

	// 1.获取当前区块，根据当前区块不断向前推，推到创世区块
	cli.QueryBlockByHash(resp.BCI.CurrentBlockHash)

	// 2.查询从零开始到现在最新的区块高度，就可以将所有的区块查询出来
	for i := uint64(0); i <= resp.BCI.Height; i++ {
		cli.QueryBlock(i)
	}
}

// 区块链交互
func channelExecute(fcn string, args [][]byte) (channel.Response, error) {
	// 表明身份
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))

	cli, err := channel.New(ctx)
	if err != nil {
		return channel.Response{}, err
	}

	//peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userRegister", "user1", "user1"]}'
	// 状态更新， insert/update/delete
	resp, err := cli.Execute(channel.Request{
		ChaincodeID: channelName,
		Fcn:         fcn,
		Args:        args,
	}, channel.WithTargetEndpoints("peer0.org1.qklszzngsy.com"))

	if err != nil {
		return channel.Response{}, nil
	}

	// 交易事件监听
	go func() {
		eventcli, err := event.New(ctx)
		if err != nil {
			panic(err)
		}

		reg, status, err := eventcli.RegisterTxStatusEvent(string(resp.TransactionID))
		defer eventcli.Unregister(reg)	// 注册必有注销

		timeoutctx, cancel := context.WithTimeout(context.Background(), time.Minute)
		defer cancel()
		for {
			select {
			case evt := <-status:
				fmt.Printf("received event of tx %s: %v", resp.TransactionID, evt)
			case <- timeoutctx.Done():
				fmt.Println("event timeout, exit!")
				return
			}
		}
	}()

	// 链码事件监听
	go func() {
		// * channel模块的监听
		reg, ccevt, err := cli.RegisterChaincodeEvent(chaincodeName, "eventname")
		if err != nil {
			return
		}
		defer cli.UnregisterChaincodeEvent(reg)

		timeoutctx, cancel := context.WithTimeout(context.Background(), time.Minute)
		defer cancel()
		for {
			select {
			case evt := <-ccevt:
				fmt.Printf("received event of tx %s: %v", resp.TransactionID, evt)
			case <- timeoutctx.Done():
				fmt.Println("event timeout, exit!")
				return
			}
		}

		// * event模块的监听
		//eventcli, err := event.New(ctx)
		//if err != nil {
		//	return
		//}

		//eventcli.RegisterChaincodeEvent(chaincodeName, "eventname")
	}()

	return resp, nil
}

func channelQuery(fcn string, args [][]byte) (channel.Response, error) {
	// 表明身份
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))

	cli, err := channel.New(ctx)
	if err != nil {
		panic(err)
	}

	// 状态的查询， select
	return cli.Query(channel.Request{
		ChaincodeID:     channelName,
		Fcn:             fcn,
		Args:            args,
	}, channel.WithTargetEndpoints("peer0.org1.qklszzngsy.com"))
}

// 事件监听
func eventHandle() {
	// 表明身份
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))

	cli, err := event.New(ctx)
	if err != nil {
		panic(err)
	}

	// 交易状态事件
	// 链码事件 业务事件
	// 区块事件
	blockEvent, events, err := cli.RegisterBlockEvent()
	if err != nil {
		panic(err)
	}
	defer cli.Unregister(blockEvent)

	timeoutctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	for {
		select {
		case evt := <-events:
			fmt.Printf("received a block", evt)
		case <- timeoutctx.Done():
			fmt.Println("event timeout, exit!")
			return
		}
	}
}
